package org.springblade.modules.system.vo.config;

import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.system.enums.DayType;
import java.util.List;

/**
 * @Author ysq
 * @Date 2025/07/10
 * @Description 时间段校验
 */
public class TimePeriodValidator {

    /**
     * 将“当日/次日+时间”转为绝对分钟数
     */
    private static int toAbsoluteMinutes(DayType dayType, String time) {
        int base = (dayType == DayType.次日) ? 1440 : 0;
        String[] parts = time.split(":");
        return base + Integer.parseInt(parts[0]) * 60 + Integer.parseInt(parts[1]);
    }

    /**
     * 获取时间段的绝对区间
     */
    private static int[] getRange(AlarmConfig.Period p) {
        int s = toAbsoluteMinutes(p.getStartDayType(), p.getStart());
        int e = toAbsoluteMinutes(p.getEndDayType(), p.getEnd());
        return new int[]{s, e};
    }

    /**
     * 判断两个时间段是否有重叠
     */
    private static boolean isPeriodOverlap(AlarmConfig.Period p1, AlarmConfig.Period p2) {
        int[] r1 = getRange(p1);
        int[] r2 = getRange(p2);
        return Math.max(r1[0], r2[0]) < Math.min(r1[1], r2[1]);
    }

    /**
     * 校验单个时间段自身合法性
     * - 如果startDayType==endDayType，要求start < end。
     * - 如果startDayType!=endDayType，要求startDayType==当日，endDayType==次日。
     * @param period 时间段
     * @throws ServiceException 不合法时抛出
     */
    public static void validatePeriodSelf(AlarmConfig.Period period) {
        DayType startType = period.getStartDayType();
        DayType endType = period.getEndDayType();
        String start = period.getStart();
        String end = period.getEnd();
		if (Func.isAnyBlank(start, end)) {
			throw new ServiceException("时间段不合法:时间不能为空");
		}
        if (startType == endType) {
            if (start.compareTo(end) >= 0) {
                throw new ServiceException("时间段不合法: " + startType + " " + start + "-" + endType + " " + end + "，同一天时开始时间必须小于结束时间");
            }
        } else {
            if (!(startType == DayType.当日 && endType == DayType.次日)) {
                throw new ServiceException("时间段不合法: " + startType + " " + start + "-" + endType + " " + end + "，跨天时必须为当日-次日");
            }
        }
    }

    /**
     * 校验整个时间段列表是否有重叠，有则抛出异常并说明哪两个时间段重叠
     * @param periods 所有时间段
     * @throws ServiceException 有重叠时抛出
     */
    public static void validateNoConflict(List<AlarmConfig.Period> periods) {
        int n = periods.size();
        for (int i = 0; i < n; i++) {
            validatePeriodSelf(periods.get(i)); // 先校验自身合法性
            for (int j = i + 1; j < n; j++) {
                if (isPeriodOverlap(periods.get(i), periods.get(j))) {
                    throw new ServiceException("时间段有重叠: "
                        + periods.get(i).getStartDayType() + " " + periods.get(i).getStart() + "-"
                        + periods.get(i).getEndDayType() + " " + periods.get(i).getEnd()
                        + " 与 "
                        + periods.get(j).getStartDayType() + " " + periods.get(j).getStart() + "-"
                        + periods.get(j).getEndDayType() + " " + periods.get(j).getEnd());
                }
            }
        }
    }

    /**
     * 判断某个时间（DayType+HH:mm）是否在某个Period时间段内
     * @param dayType 时间的当日/次日
     * @param time 时间（HH:mm）
     * @param period 时间段
     * @return 是否在时间段内
     */
    public static boolean isTimeInPeriod(DayType dayType, String time, AlarmConfig.Period period) {
        validateTimeFormat(time);
        int point = toAbsoluteMinutes(dayType, time);
        int[] range = getRange(period);
        return point >= range[0] && point < range[1];
    }

    /**
     * 判断某个时间（DayType+HH:mm）是否在多个Period时间段（List<Period>）中的任意一个内
     * @param dayType 时间的当日/次日
     * @param time 时间（HH:mm）
     * @param periods 时间段列表
     * @return 是否在任意一个时间段内
     */
    public static boolean isTimeInPeriods(DayType dayType, String time, List<AlarmConfig.Period> periods) {
        validateTimeFormat(time);
        if (periods == null || periods.isEmpty()) {
            return false;
        }
        for (AlarmConfig.Period period : periods) {
            if (isTimeInPeriod(dayType, time, period)) {
                return true;
            }
        }
        return false;
    }

    private static void validateTimeFormat(String time) {
        if (time == null || !time.matches("^([01]\\d|2[0-3]):[0-5]\\d$")) {
            throw new ServiceException("时间格式不合法，必须为HH:mm，且在00:00-23:59之间，实际传入：" + time);
        }
    }
}
